﻿using Crgd.UAuthServer.Core.Extensions;
using Furion.EventBus;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Crgd.UAuthServer.Core
{
    /// <summary>
    /// 可重试处理程序，单例服务
    /// </summary>
    public class RetryableHandle
    {
        public RetryableHandle(
            IEventPublisher eventPublisher,
            IServiceProvider serviceProvider)
        {
            this.serviceProvider = serviceProvider;
            this.eventPublisher = eventPublisher;
        }

        private IServiceProvider serviceProvider;
        private IEventPublisher eventPublisher;

        /// <summary>
        /// 启动一个可重试程序，该程序在一个新了域中执行（重试亦是如此）
        /// 如果失败，将重试
        /// </summary>
        /// <returns></returns>
        public async Task RunAsync(Func<IServiceProvider, Task> handler, long baseWaitSeconds = 30, int maxRetryTimes = 10, float multiplying = 2)
        {
            var errorTimes = 0;
            while (true)
            {
                try
                {
                    await serviceProvider.RunInNewScopeAsync(handler);
                    _ = this.eventPublisher.PublishAsync(RetryableEvents.Successed, new RetryablePayload
                    {
                        Event = RetryableEvents.Successed,
                        RetryTimes = errorTimes + 1
                    });
                    break;
                }
                catch (Exception ex)
                {
                    errorTimes++;
                    _ = this.eventPublisher.PublishAsync(RetryableEvents.Error, new RetryablePayload
                    {
                        Event = RetryableEvents.Error,
                        RetryTimes = errorTimes + 1,
                        Error = ex
                    });
                    if (errorTimes > maxRetryTimes)
                    {
                        _ = this.eventPublisher.PublishAsync(RetryableEvents.Failure, new RetryablePayload
                        {
                            Event = RetryableEvents.Failure,
                            RetryTimes = errorTimes + 1,
                            Error = ex
                        });
                        break;
                    }
                    await Task.Delay(TimeSpan.FromSeconds(errorTimes * multiplying * baseWaitSeconds));
                }
            }
        }
    }
}
